﻿using Gym.Domain.AdminAggregate;
using Gym.Domain.Common;
using Gym.Domain.ParticipantAggregate;
using Gym.Domain.RoomAggregate;
using Gym.Domain.SessionAggregate;
using Gym.Domain.SubscriptionAggregate;
using Gym.Domain.TrainerAggregate;
using Gym.Infrastructure.Middleware;
using Microsoft.AspNetCore.Http;
using Microsoft.EntityFrameworkCore;
using System.Reflection;
using G = Gym.Domain.GymAggregate;

namespace Gym.Infrastructure.Persistence
{
    public class GymDbContext : DbContext
    {
        private readonly IHttpContextAccessor _httpContextAccessor;

        public DbSet<Subscription> Subscriptions { get; set; } = null!;
        public DbSet<G.Gym> Gyms { get; set; } = null!;
        public DbSet<Room> Rooms { get; set; } = null!;
        public DbSet<Session> Sessions { get; set; } = null!;
        public DbSet<Trainer> Trainers { get; set; } = null!;
        public DbSet<Participant> Participants { get; set; } = null!;
        public DbSet<Admin> Admins { get; set; } = null!;

        public GymDbContext(DbContextOptions<GymDbContext> options, IHttpContextAccessor httpContextAccessor)
            : base(options)
        {
            _httpContextAccessor = httpContextAccessor;
        }

        protected override void OnModelCreating(ModelBuilder modelBuilder)
        {
            modelBuilder.ApplyConfigurationsFromAssembly(Assembly.GetExecutingAssembly());
            base.OnModelCreating(modelBuilder);
        }

        public override async Task<int> SaveChangesAsync(CancellationToken cancellationToken = default)
        {
            var domainEvents = ChangeTracker.Entries<AggregateRoot>()
                .Select(entry => entry.Entity.PopDomainEvents())
                .SelectMany(x => x)
                .ToList();

            var result = await base.SaveChangesAsync(cancellationToken);
            
            Queue<IDomainEvent> domainEventsQueue = _httpContextAccessor.HttpContext.Items.TryGetValue(EventualConsistencyMiddleware.DomainEventsKey, out var value) &&
            value is Queue<IDomainEvent> existingDomainEvents
                ? existingDomainEvents
                : new();

            domainEvents.ForEach(domainEventsQueue.Enqueue);
            _httpContextAccessor.HttpContext.Items[EventualConsistencyMiddleware.DomainEventsKey] = domainEventsQueue;
            return result;
        }
    }
}
